﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Newtonsoft.Json;
using DownloadBook.Server;
using QY.ThreadPool;
using DownloadBook.Entity;

namespace DownloadBook {
    public partial class FrmMain : Form {
        private string m_OriginTitle;
        private readonly List<Content> m_ContentList = new List<Content>();
        private DownloadContent downloadContent;
        private Book book = new Book();
        private readonly List<Book> bookHistoryList = new List<Book>();
        private string outputDirectoryPath;
        private string bookHistoryPath;
        private string jsonHistory;
        private ProxyConfig proxyConfig = new ProxyConfig();

        public FrmMain() {
            InitializeComponent();
        }


        private void FrmMain_Load(object sender, EventArgs e) {
            ThreadPool.SetMinThreads(30, 10);
            SelectAllSupport.Bind(txtOriginHtml);
            SelectAllSupport.Bind(comBookName);
            SelectAllSupport.Bind(txtURL);

            //创建对象池工具
            QYThreadPoolManager threadPoolManager = new QYThreadPoolManager(300);
            QYThreadPoolManager.defaultManager = threadPoolManager;
            QYThreadPoolManager.defaultPool = threadPoolManager.CreateThreadPool(300);
            threadPoolManager.Init();

            m_OriginTitle = this.Text;
            comMaxThreadCount.SelectedItem = "10";
            lblMsg.Text = "准备就绪";

            outputDirectoryPath = Path.Combine(Environment.CurrentDirectory, "Html");
            bookHistoryPath = Path.Combine(Environment.CurrentDirectory, "History.json");

            this.LoadConfig();
            this.LoadHistory();
        }

        /// <summary>
        /// 载入配置
        /// </summary>
        protected void LoadConfig() {
            this.proxyConfig = ProxyConfig.LoadFromFile();
        }

        /// <summary>
        /// 载入历史
        /// </summary>
        protected void LoadHistory(){
            this.bookHistoryList.Clear();

            if (File.Exists(bookHistoryPath)) {
                //读取历史配置信息
                jsonHistory = File.ReadAllText(bookHistoryPath);

                //反序列化
                List<Book> list = JsonConvert.DeserializeObject<List<Book>>(jsonHistory);
                this.bookHistoryList.AddRange(list);

                //添加到界面显示
                for(int i = 0; i < list.Count; i++) {
                    this.comBookName.Items.Add(list[i]);
                }
            }
        }

        /// <summary>
        /// 保存历史
        /// </summary>
        /// <param name="addNow">是否添加当前的</param>
        public void SaveHistory(bool addNow = true){
            //监测记录新元素
            if (addNow) {//是否添加当前的
                Book book = this.bookHistoryList.Find((x) => x.url == this.book.url);
                if (book != null) {
                    //更新信息
                    book.LoadBasic(this.book);
                }else{
                    book = new Book();

                    //更新信息
                    book.LoadBasic(this.book);

                    this.bookHistoryList.Add(book);
                    this.bookHistoryList.Sort();

                    //重建
                    comBookName.Items.Clear();
                    comBookName.Items.AddRange(this.bookHistoryList.ToArray());
                    comBookName.SelectedItem = book;
                }
            }
            
            //序列化为 json 文件
            string json = JsonConvert.SerializeObject(this.bookHistoryList, Formatting.Indented);

            if(!string.Equals(jsonHistory, json)){//如果 json 文件内容确实发生了变化
                //创建文件夹
                if(!Directory.Exists(outputDirectoryPath)){
                    Directory.CreateDirectory(outputDirectoryPath);
                }

                //写入文件
                File.WriteAllText(bookHistoryPath, json);

                //更新记录
                jsonHistory = json;
            }
        }




        /// <summary>
        /// 变更下载状态
        /// </summary>
        /// <param name="isDownloading"></param>
        private void AlertDownloadState(bool isDownloading) {
            this.Invoke(new EventHandler(delegate {
                bool enabled = !isDownloading;
                this.btnDownloadContext.Enabled = enabled;
                this.BtnDownloadHtml.Enabled = enabled;
                this.comMaxThreadCount.Enabled = enabled;
                this.btnExtractMenuItem.Enabled = enabled;
                this.comBookName.Enabled = enabled;
                this.txtURL.Enabled = enabled;
                this.btnMergeContent.Enabled = enabled;
                this.btnSort.Enabled = enabled;
                this.btnReverseOrder.Enabled = enabled;
            }));
        }

        /// <summary>
        /// 下载章节列表
        /// </summary>
        private void DownloadContent(string bookName, string url, int maxThreadCount) {
            if (string.IsNullOrEmpty(bookName)) {
                ShowErrorMsg("请先输入书名");
            } else {
                AlertDownloadState(true);
                string directory = GetDownloadDirectory(bookName);
                bool submiting = false;
                string lastMsg = "";

                void actionUpdateLblMsgText(string msg) {
                    lock (this) {
                        lastMsg = msg;
                        if (!submiting) {
                            submiting = true;
                            this.BeginInvoke(new EventHandler(delegate {
                                lock (this) {
                                    submiting = false;
                                    lblMsg.Text = lastMsg;
                                }
                            }));
                        }
                    }
                }

                actionUpdateLblMsgText("准备开始下载");

                downloadContent = new DownloadContent(this.proxyConfig, directory, url, m_ContentList, maxThreadCount, actionUpdateLblMsgText, () => {
                    AlertDownloadState(false);
                });
                downloadContent.Start();
            }
        }

        /// <summary>
        /// 获取下载文件夹
        /// </summary>
        /// <param name="bookName"></param>
        /// <returns></returns>
        private string GetDownloadDirectory(string bookName) {
            return Path.Combine(this.outputDirectoryPath, bookName);
        }

        /// <summary>
        /// 新线程
        /// </summary>
        /// <param name="action"></param>
        private void NewThreadTask(Action action) {
            Thread thread = new Thread(new ThreadStart(action)) {
                IsBackground = true
            };
            thread.Start();
        }

        /// <summary>
        /// 显示错误信息
        /// </summary>
        /// <param name="msg"></param>
        private void ShowErrorMsg(string msg) {
            ShowErrorMsg(msg, "出错了");
        }

        /// <summary>
        /// 显示错误信息
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="title"></param>
        private void ShowErrorMsg(string msg, string title) {
            MessageBox.Show(msg, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }


        /// <summary>
        /// 搜索目录元素
        /// </summary>
        private void SearchMenuItem() {
            if (chkHideNotExistsInSearchResurt.Checked) {
                ShowSearchMenuItemsHideNotExistResult();
            } else {
                ShowSearchMenuItemsOnlySelected();
            }
        }

        /// <summary>
        /// 显示搜索结果，隐藏不在搜索结果中的对象
        /// </summary>
        private void ShowSearchMenuItemsHideNotExistResult() {
            string searchKey = txtSearchMenuItem.Text;
            listMenuItemList.Items.Clear();
            listMenuItemList.ClearSelected();

            if (!string.IsNullOrEmpty(searchKey)) {
                for (int i = 0; i < m_ContentList.Count; i++) {
                    Content content = m_ContentList[i];
                    if (content.title.Contains(searchKey)) {
                        listMenuItemList.Items.Add(content);
                        listMenuItemList.SetSelected(listMenuItemList.Items.Count - 1, true);
                    }
                }
            }
        }

        /// <summary>
        /// 显示搜索结果，只选中，不移除
        /// </summary>
        private void ShowSearchMenuItemsOnlySelected() {
            string searchKey = txtSearchMenuItem.Text;
            listMenuItemList.Items.Clear();
            listMenuItemList.ClearSelected();
            for (int i = 0; i < m_ContentList.Count; i++) {
                Content content = m_ContentList[i];
                listMenuItemList.Items.Add(content);
                if (!string.IsNullOrEmpty(searchKey)) {//如果有输入搜索内容
                    if (content.title.Contains(searchKey)) {
                        listMenuItemList.SetSelected(i, true);
                    }
                }
            }
        }


        /// <summary>
        /// 移除选中的目录元素
        /// </summary>
        private void RemoveSelectedMenuItem() {
            int removeItemCount = listMenuItemList.SelectedItems.Count;
            if (removeItemCount > 0) {
                string title = "删除提示";
                string text;
                if (removeItemCount == 1) {
                    text = string.Format("您确定要删除章节：{0} 吗？", listMenuItemList.SelectedItem);
                } else {
                    text = "您确定要移除选中的章节列表吗？选中的章节个数：" + removeItemCount;
                }
                if (DialogResult.Yes == MessageBox.Show(text, title, MessageBoxButtons.YesNo, MessageBoxIcon.Information)) {
                    while (listMenuItemList.SelectedItems.Count > 0) {
                        object item = listMenuItemList.SelectedItems[0];
                        listMenuItemList.Items.Remove(item);
                        m_ContentList.Remove(item as Content);
                    }
                }
            }
        }

        /// <summary>
        /// 目录元素列表窗口右键菜单加载事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ContextMenuListMenuItemSearchResult_Opening(object sender, CancelEventArgs e) {
            int selectedItemCount = listMenuItemList.SelectedItems.Count;
            int itemsCount = listMenuItemList.Items.Count;
            bool selectedOne = selectedItemCount == 1;
            //只选择一个元素时可用的菜单
            menuOpenOriginURL.Enabled = selectedOne;//打开原始URL，只选一个章节时可用
            menuItemDeleteSelectedToolStripMenuItem.Enabled = selectedItemCount > 0;//删除。有选择才能用
            menuCopyTitleToolStripMenuItem.Enabled = selectedOne;//复制标题

            //全选 - 反选 - 全不选
            menuSelectAllToolStripMenuItem.Enabled = itemsCount > 0 && selectedItemCount < itemsCount;//全选，有元素并且还未全选时可用
            menuSelectAllNotToolStripMenuItem.Enabled = itemsCount > 0 && selectedItemCount > 0;//全不选，有元素并且有选择了才可用
            menuSelectReverseToolStripMenuItem.Enabled = itemsCount > 0;//反选，只要有元素就可用

            //向上选择、向下选择
            menuSelectToStartToolStripMenuItem.Enabled = selectedOne;//向上选择到开始
            menuSelectToEndToolStripMenuItem.Enabled = selectedOne;//向下选择到结束
        }



        /// <summary>
        /// 按钮 - 删除小说
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnDeleteBook_Click(object sender, EventArgs e) {
            string url = txtURL.Text;
            string bookName = this.comBookName.Text;
            if (string.IsNullOrEmpty(bookName)) {
                ShowErrorMsg("请先输入书名");
            } else if (string.IsNullOrEmpty(url)) {
                ShowErrorMsg("Url不能为空");
            } else {
                if(DialogResult.OK == MessageBox.Show($"您确定要删除 {bookName} 这本书吗？", "删除？", MessageBoxButtons.OKCancel, MessageBoxIcon.Question)) {
                    //删除文件夹
                    string directory = GetDownloadDirectory(bookName);
                    if (Directory.Exists(directory)) {
                        Directory.Delete(directory, true);
                    }

                    //删除历史
                    if(this.bookHistoryList.Find((x) => x.url == url) is Book book) {
                        //删除历史
                        this.bookHistoryList.Remove(book);

                        //更新历史配置文件
                        this.SaveHistory(false);

                        //更新UI
                        this.comBookName.Items.Remove(book);
                        this.comBookName.Text = "";
                        this.txtURL.Text = "";
                    }
                }
            }
        }

        /// <summary>
        /// 按钮 - 下载HTML
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnDownloadHtml_Click(object sender, EventArgs e) {
            string url = txtURL.Text;
            string bookName = this.comBookName.Text;
            if (string.IsNullOrEmpty(bookName)) {
                ShowErrorMsg("请先输入书名");
            } else if (string.IsNullOrEmpty(url)) {
                ShowErrorMsg("Url不能为空");
            }else {
                if(!string.IsNullOrEmpty(book.url) && !string.Equals(book.url, txtURL.Text, StringComparison.OrdinalIgnoreCase)) {
                    this.book = new Book();
                }
                //更新信息
                book.name = this.GetBookName();
                book.url = txtURL.Text;

                //先保存历史
                this.SaveHistory();

                txtOriginHtml.Text = "准备下载中";
                NewThreadTask(() => {
                    string html =  HttpUtil.HttpGet(url, this.proxyConfig);

                    string directory = GetDownloadDirectory(bookName);
                    if (!Directory.Exists(directory)) {
                        Directory.CreateDirectory(directory);
                    }
                    string filePath = Path.Combine(directory, "index.html");
                    File.WriteAllText(filePath, html);

                    this.Invoke( new EventHandler(delegate {
                        txtOriginHtml.Text = html;
                    }));
                });
            }
        }

        /// <summary>
        /// 按钮 - 载入本地
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnLoadLocal_Click(object sender, EventArgs e) {
            string url = txtURL.Text;
            string bookName = this.comBookName.Text;
            if (string.IsNullOrEmpty(bookName)) {
                ShowErrorMsg("请先输入书名");
            } else if (string.IsNullOrEmpty(url)) {
                ShowErrorMsg("Url不能为空");
            } else {
                //更新信息
                book.name = this.GetBookName();
                book.url = txtURL.Text;

                string directory = GetDownloadDirectory(bookName);
                string filePath = Path.Combine(directory, "index.html");
                string html = "";
                if (!File.Exists(filePath)) {
                    ShowErrorMsg("不存在目标本地文件");
                } else {
                    html = File.ReadAllText(filePath);
                }
                txtOriginHtml.Text = html;
            }
        }


        /// <summary>
        /// 按钮 - 提取目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnExtractMenuItem_Click(object sender, EventArgs e) {
            if (string.IsNullOrEmpty(txtURL.Text)) {
                ShowErrorMsg("请先输入URL");
            } else {
                Regex regex = new Regex("<a[^>]*?>([\\w\\W]*?)</a>", RegexOptions.IgnoreCase);
                int groupId = 0;
                Match match = regex.Match(txtOriginHtml.Text);
                if (!match.Success) {
                    lblMsg.Text = "提取失败，请重新设置提取目录的正则表达式";
                } else if (match.Groups.Count < groupId) {
                    lblMsg.Text = "提取失败，分组不够，分组id不正确";
                } else {
                    m_ContentList.Clear();
                    listMenuItemList.Items.Clear();

                    string originUrl = txtURL.Text;
                    while (match.Success) { 
                        string item = match.Groups[groupId].ToString();

                        string href = HtmlUtil.GetAHref(item);
                        string url = HtmlUtil.linkCalculation(originUrl, href);
                        string title = HtmlUtil.GetATextOutHtml(item);
                        Content content = new Content {
                            title = title,
                            url = url
                        };


                        m_ContentList.Add(content);
                        match = match.NextMatch();
                    }
                    SearchMenuItem();
                }

                MessageBox.Show(string.Format("提取完成，一共{0}个元素", m_ContentList.Count) , "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        /// <summary>
        /// 获取小说名称
        /// </summary>
        /// <returns></returns>
        private string GetBookName() {
            string bookName = comBookName.Text;
            string str = this.book.name + " - " + this.book.author;
            if(str == bookName) {//如果其实并没有发生改变，仅仅是显示不同的内容格式而已
                bookName = this.book.name;//那么返回原始名称就行了
            }
            return bookName;
        }

        /// <summary>
        /// 按钮 - 搜索目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSearchMenuItem_Click(object sender, EventArgs e) {
            SearchMenuItem();
        }


        /// <summary>
        /// 按钮 - 下载内容
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnDownloadContext_Click(object sender, EventArgs e) {
            if (this.m_ContentList.Count < 1) {
                ShowErrorMsg("请先提取章节目录元素");
            } else {
                int maxThreadCount = Math.Max(1, Convert.ToInt32(comMaxThreadCount.Text));
                string bookName = comBookName.Text;
                string url = txtURL.Text;
                this.Text = string.Format("{0} - {1}", bookName, m_OriginTitle);
                NewThreadTask(() => {
                    DownloadContent(bookName, url, maxThreadCount);
                });
            }
        }

        /// <summary>
        /// 按钮 - 合并内容
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnMergeContent_Click(object sender, EventArgs e) {
            if (string.IsNullOrEmpty(txtURL.Text)) {
                ShowErrorMsg("请先输入网址");
            } else if (string.IsNullOrEmpty(comBookName.Text)) {
                ShowErrorMsg("请先输入书名");
            } else if (m_ContentList.Count < 1) {
                ShowErrorMsg("请先提取章节目录元素");
            } else {
                book.contentList = m_ContentList;
                book.htmlDirectory = GetDownloadDirectory(book.name);
                using(FrmMergeContent frmMergeContent = new FrmMergeContent(this, book)) {
                    frmMergeContent.ShowDialog();
                }
            }
        }
        
        /// <summary>
        /// 重新排序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSort_Click(object sender, EventArgs e) {
            for(int i = 0; i < this.m_ContentList.Count; i++) {
                Content content = this.m_ContentList[i];
                content.InitTitleIndex();
            }
            this.m_ContentList.Sort();

            SearchMenuItem();
        }
        
        /// <summary>
        /// 倒序排序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnReverseOrder_Click(object sender, EventArgs e) {
            int end = this.m_ContentList.Count / 2;
            for (int index1 = 0; index1 < end; index1++) {
                int index2 = this.m_ContentList.Count - index1 - 1;
                Content contentBackup = this.m_ContentList[index2];
                this.m_ContentList[index2] = this.m_ContentList[index1];
                this.m_ContentList[index1] = contentBackup;
            }
            SearchMenuItem();
        }
        
        /// <summary>
        /// 按钮 - 停止下载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnStopDownload_Click(object sender, EventArgs e) {
            if (downloadContent != null) {
                downloadContent.Stop();
            }
        }

        /// <summary>
        /// 按钮 - 打开输出目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnOpenOutputDirectory_Click(object sender, EventArgs e) {
            string outDirectory = Path.Combine(Environment.CurrentDirectory, "txt");
            Process.Start("explorer.exe", outDirectory);
        }
        

        /// <summary>
        /// 按钮 - 代理设置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnProxySetting_Click(object sender, EventArgs e) {
            using (FrmProxySetting frmProxySetting = new FrmProxySetting(this.proxyConfig)) {
                frmProxySetting.ShowDialog();
            }
        }

        /// <summary>
        /// 列表菜单项 键盘按下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListMenuItemList_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Delete) {
                RemoveSelectedMenuItem();
            }
        }

        /// <summary>
        /// 菜单 - 目录元素删除选中的元素
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemDeleteSelectedToolStripMenuItem_Click(object sender, EventArgs e) {
            RemoveSelectedMenuItem();
        }

        /// <summary>
        /// 菜单 - 浏览器中打开原始链接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuOpenOriginURL_Click(object sender, EventArgs e) {
            if(listMenuItemList.SelectedItems.Count == 1) {
                if (listMenuItemList.SelectedItem is Content content) {
                    Process.Start("explorer.exe",$"\"{content.url}\"");
                }
            }
        }

        /// <summary>
        /// 菜单 - 全选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuSelectAllToolStripMenuItem_Click(object sender, EventArgs e) {
            for(int i = 0; i < listMenuItemList.Items.Count; i++) {
                listMenuItemList.SetSelected(i, true);
            }
        }

        /// <summary>
        /// 菜单 - 反选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuSelectReverseToolStripMenuItem_Click(object sender, EventArgs e) {
            for (int i = 0; i < listMenuItemList.Items.Count; i++) {
                listMenuItemList.SetSelected(i, !listMenuItemList.GetSelected(i));//切换选中状态
            }
        }

        /// <summary>
        /// 菜单 - 全不选
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuSelectAllNotToolStripMenuItem_Click(object sender, EventArgs e) {
            listMenuItemList.ClearSelected();
        }

        /// <summary>
        /// 菜单 - 向上选择到开始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuSelectToStartToolStripMenuItem_Click(object sender, EventArgs e) {
            int endIndex = listMenuItemList.SelectedIndex;
            for (int i = 0; i < endIndex; i++) {
                listMenuItemList.SetSelected(i, true);
            }
        }

        /// <summary>
        /// 菜单 - 向下选择到结束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuSelectToEndToolStripMenuItem_Click(object sender, EventArgs e) {
            for (int i = listMenuItemList.SelectedIndex + 1; i < listMenuItemList.Items.Count; i++) {
                listMenuItemList.SetSelected(i, true);
            }
        }
        
        /// <summary>
        /// 菜单 - 复制标题
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuCopyTitleToolStripMenuItem_Click(object sender, EventArgs e) {
            if(listMenuItemList.SelectedItems.Count == 1) {
                if (listMenuItemList.SelectedItem is Content content) {
                    Clipboard.Clear();
                    Clipboard.SetText(content.title);
                }
            }
        }
        
        /// <summary>
        /// 菜单 - 上移
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MoveUpToolStripMenuItem_Click(object sender, EventArgs e) {
            List<Content> list = new List<Content>();
            list.AddRange(listMenuItemList.SelectedItems.Cast<Content>());

            //循环处理
            for (int i = 0; i < list.Count; i++) {
                Content content = list[i];
                int index = this.m_ContentList.IndexOf(content);
                if(index > 0) {
                    //先移除
                    this.m_ContentList.RemoveAt(index);
                    listMenuItemList.Items.RemoveAt(index);

                    //再插入
                    int indexTarget = index - 1;
                    this.m_ContentList.Insert(indexTarget, content);
                    listMenuItemList.Items.Insert(indexTarget, content);

                    //恢复选中状态
                    listMenuItemList.SetSelected(indexTarget, true);
                }
            }
        }
        

        /// <summary>
        /// 菜单 - 下移
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MoveDownToolStripMenuItem_Click(object sender, EventArgs e) {
            List<Content> list = new List<Content>();
            list.AddRange(listMenuItemList.SelectedItems.Cast<Content>());

            //循环处理
            for (int i = list.Count - 1; i >= 0; i--) {
                Content content = list[i];
                int index = this.m_ContentList.IndexOf(content);
                if(index > -1 && index < this.m_ContentList.Count - 1) {
                    //先移除
                    this.m_ContentList.RemoveAt(index);
                    listMenuItemList.Items.RemoveAt(index);

                    //再插入
                    int indexTarget = index + 1;
                    this.m_ContentList.Insert(indexTarget, content);
                    listMenuItemList.Items.Insert(indexTarget, content);

                    //恢复选中状态
                    listMenuItemList.SetSelected(indexTarget, true);
                }
            }
        }


        /// <summary>
        /// 菜单 - 上移到开始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MoveToStartToolStripMenuItem_Click(object sender, EventArgs e) {
            List<Content> list = new List<Content>();
            list.AddRange(listMenuItemList.SelectedItems.Cast<Content>());

            //循环处理
            for (int i = 0; i < list.Count; i++) {
                Content content = list[i];
                int index = this.m_ContentList.IndexOf(content);
                if(index > -1) {
                    //先移除
                    this.m_ContentList.RemoveAt(index);
                    listMenuItemList.Items.RemoveAt(index);

                    //再插入
                    this.m_ContentList.Insert(i, content);
                    listMenuItemList.Items.Insert(i, content);

                    //恢复选中状态
                    listMenuItemList.SetSelected(i, true);
                }
            }
        }

        /// <summary>
        /// 菜单 - 下移到末尾
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MoveToEndToolStripMenuItem_Click(object sender, EventArgs e) {
            List<Content> list = new List<Content>();
            list.AddRange(listMenuItemList.SelectedItems.Cast<Content>());

            //循环处理
            for (int i = 0; i < list.Count; i++) {
                Content content = list[i];
                int index = this.m_ContentList.IndexOf(content);
                if(index > -1 && index < this.m_ContentList.Count - 1) {
                    //先移除
                    this.m_ContentList.RemoveAt(index);
                    listMenuItemList.Items.RemoveAt(index);

                    //再末尾添加
                    this.m_ContentList.Add(content);
                    listMenuItemList.Items.Add(content);

                    //恢复选中状态
                    listMenuItemList.SetSelected(listMenuItemList.Items.Count - 1, true);
                }
            }
        }


        /// <summary>
        /// 菜单 - 关于
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LblAbout_Click(object sender, EventArgs e) {
            string url = "https://gitee.com/fjqingyou/DownloadBook/issues";
            Process.Start(url);
        }

        /// <summary>
        /// 本地搜索 回车键支持
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TxtSearchMenuItem_KeyUp(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Enter) {
                btnSearchMenuItem.PerformClick();//模拟点击按钮
            }
        }

        /// <summary>
        /// 下拉框 - 书名
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ComBookName_SelectedIndexChanged(object sender, EventArgs e) {
            if(this.comBookName.SelectedItem is Book book){
                //载入基础信息
                this.book.LoadBasic(book);

                //更新 UI 上的 URL
                this.txtURL.Text = book.url;
            }
        }
    }
}
